import { reactive } from 'vue'
import store from '@/store'

/**
 * WebSocket 封装类
 * 功能特性：
 * 1. 自动重连机制（支持指数退避）
 * 2. 心跳检测保活
 * 3. 消息队列（断网自动缓存）
 * 4. 多类型事件监听
 * 5. 完善的连接状态管理
 */
class WebSocketClient {
  /**
   * 构造函数
   * @param {string} url WebSocket 连接地址
   * @param {Object} [options] 配置选项
   * @param {number} [options.reconnectLimit=5] 最大重连次数
   * @param {number} [options.reconnectInterval=3000] 基础重连间隔(ms)
   * @param {number} [options.heartbeatInterval=25000] 心跳间隔(ms)
   * @param {number} [options.heartbeatTimeout=5000] 心跳响应超时时间(ms)
   * @param {boolean} [options.autoConnect=true] 是否自动连接
   */
  constructor(url, options = {}) {
    // 合并配置选项
    this.config = {
      url,
      reconnectLimit: options.reconnectLimit || 5,
      reconnectInterval: options.reconnectInterval || 3000,
      heartbeatInterval: options.heartbeatInterval || 25000,
      heartbeatTimeout: options.heartbeatTimeout || 5000,
      autoConnect: options.autoConnect !== false, // 默认true
      debug: options.debug || false,
      ...options
    }

    // 响应式状态管理
    this.state = reactive({
      isConnected: false, // 当前是否已连接
      isConnecting: false, // 是否正在连接中
      lastActivity: null, // 最后一次活动时间戳
      reconnectCount: 0 // 当前重连次数
    })

    // 响应式数据管理
    this.data = reactive({
      lastMessage: null, // 最后收到的消息
      lastError: null, // 最后的错误信息
      pendingMessages: [] // 待发送消息队列
    })

    // 内部变量
    this.ws = null // WebSocket 实例
    this.heartbeatTimer = null // 心跳定时器
    this.pongTimeout = null // 心跳响应超时定时器
    this.messageHandlers = new Map() // 消息处理器集合
    this.eventListeners = new Map() // 事件监听器集合
    this.reconnectTimer = null // 新增重连定时器引用
    this.manualDisconnect = false //

    // 自动初始化连接
    if (this.config.autoConnect) {
      this.connect()
    }
  }

  /* ----------------- 公共 API ----------------- */

  /**
   * 建立 WebSocket 连接
   * @return {void}
   */
  connect() {
    // 防止重复连接
    if (this.state.isConnected || this.state.isConnecting || this.reconnectTimer) {
      console.warn('[WebSocket] 连接已存在或正在建立')
      return
    }

    this.state.isConnecting = true
    this.data.lastError = null

    try {
      // 创建 WebSocket 实例
      const token = store.getters.token
      this.ws = new WebSocket(this.config.url + '?cid=' + Date.now() + '&uid=' + token)

      // 绑定事件处理器
      this.ws.onopen = () => this.handleOpen()
      this.ws.onmessage = (e) => this.handleMessage(e)
      this.ws.onerror = (e) => this.handleError(e)
      this.ws.onclose = (e) => this.handleClose(e)
    } catch (error) {
      this.handleError(error)
    }
  }

  /**
   * 断开 WebSocket 连接
   * @return {void}
   */
  disconnect() {
    console.warn('[WebSocket] 断开 WebSocket 连接')
    // 清除重连定时器
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }

    // 清理所有定时器
    this.clearTimers()

    // 关闭连接
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }

    // 更新状态
    this.state.isConnected = false
    this.state.isConnecting = false
  }

  /**
   * 发送消息
   * @param {any} data 要发送的数据
   * @param {Object} [options] 发送选项
   * @param {boolean} [options.retry=true] 是否在断网时重试
   * @return {boolean} 是否发送成功
   */
  send(data, { retry = true } = {}) {
    // 未连接时的处理
    if (!this.state.isConnected) {
      if (retry) {
        // 存入待发送队列并尝试重连
        this.data.pendingMessages.push(data)
        this.attemptReconnect()
      }
      return false
    }

    try {
      // 序列化数据
      const payload = typeof data === 'string' ? data : JSON.stringify(data)
      this.ws.send(payload)
      return true
    } catch (error) {
      // 发送失败处理
      this.handleError(error)
      if (retry) this.data.pendingMessages.push(data)
      return false
    }
  }

  /**
   * 添加消息监听
   * @param {string} event 事件类型 ('message' 或 'message:xxx')
   * @param {Function} handler 处理函数
   * @return {Function} 取消监听函数
   */
  on(event, handler) {
    // 初始化事件集合
    if (!this.messageHandlers.has(event)) {
      this.messageHandlers.set(event, new Set())
    }

    // 添加处理器
    this.messageHandlers.get(event).add(handler)

    // 返回取消监听函数
    return () => this.off(event, handler)
  }

  /**
   * 移除消息监听
   * @param {string} event 事件类型
   * @param {Function} handler 处理函数
   * @return {void}
   */
  off(event, handler) {
    if (this.messageHandlers.has(event)) {
      this.messageHandlers.get(event).delete(handler)
    }
  }

  /**
   * 一次性消息监听
   * @param {string} event 事件类型
   * @param {Function} handler 处理函数
   * @return {void}
   */
  once(event, handler) {
    const onceHandler = (data) => {
      handler(data)
      this.off(event, onceHandler)
    }
    this.on(event, onceHandler)
  }

  /* ----------------- 内部方法 ----------------- */

  /**
   * 处理连接打开事件
   * @private
   */
  handleOpen() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }

    // 更新状态
    this.state.isConnected = true
    this.state.isConnecting = false
    this.state.reconnectCount = 0
    this.state.lastActivity = Date.now()

    // 触发连接事件
    this.emit('connected')

    // 启动心跳检测
    this.startHeartbeat()

    // 发送积压的消息
    this.flushPendingMessages()
  }

  /**
   * 处理收到消息事件
   * @param {MessageEvent} event 消息事件
   * @private
   */
  handleMessage(event) {
    // 更新最后活动时间
    this.state.lastActivity = Date.now()
    // console.warn('[WebSocket] 处理收到消息事件:' + event.data + ', type:' + typeof event.data)

    try {
      // 处理心跳响应（简化处理）
      if (event.data === 'pong' || (typeof event.data === 'string' && event.data.match(/pong/i))) {
        clearTimeout(this.pongTimeout)
        this.pongTimeout = null
        return
      }

      // 解析消息数据
      const data = JSON.parse(event.data)
      this.data.lastMessage = data

      // 心跳响应处理
      if (data.type === 'pong') {
        clearTimeout(this.pongTimeout)
        this.pongTimeout = null
        return
      }

      // 触发消息事件
      this.emit('message', data)

      // 触发特定类型消息事件
      if (data.type) {
        this.emit(`message:${data.type}`, data)
      }
    } catch (error) {
      // 消息解析错误处理
      this.emit('error', {
        type: 'parseError',
        error,
        rawData: event.data
      })
    }
  }

  /**
   * 处理错误事件
   * @param {Error} error 错误对象
   * @private
   */
  handleError(error) {
    // 更新状态
    this.state.isConnecting = false
    this.data.lastError = error

    // 如果不是手动断开，则尝试重连
    if (!this.manualDisconnect) {
      this.attemptReconnect()
    }

    // 触发错误事件
    this.emit('error', error)
  }

  /**
   * 处理连接关闭事件
   * @param {CloseEvent} event 关闭事件
   * @private
   */
  handleClose(event) {
    // 清理定时器
    this.clearTimers()

    // 更新状态
    this.state.isConnected = false
    this.state.isConnecting = false

    // 触发断开事件
    this.emit('disconnected', event)

    // 尝试重连
    this.attemptReconnect()
  }

  /**
   * 尝试重新连接
   * @private
   */
  attemptReconnect() {
    console.warn('[WebSocket] 尝试重新连接')
    // 如果已有重连计划或已达上限，则放弃
    if (this.reconnectTimer || this.state.reconnectCount >= this.config.reconnectLimit) {
      this.emit('reconnectFailed')
      console.warn('[WebSocket] reconnectFailed')
      return
    }

    // 增加重连计数
    this.state.reconnectCount++

    // 计算下次重连延迟（指数退避算法）
    const delay = Math.min(
      this.config.reconnectInterval * this.state.reconnectCount,
      30000 // 最大延迟30秒
    )

    // 设置重连定时器
    this.reconnectTimer = setTimeout(() => {
      this.reconnectTimer = null
      if (!this.state.isConnected) {
        this.connect()
      }
    }, delay)
  }

  /**
   * 启动心跳检测
   * @private
   */
  startHeartbeat() {
    // 清理现有定时器
    this.clearTimers()

    // 设置心跳定时器
    this.heartbeatTimer = setInterval(() => {
      // 只有连接正常时才发送心跳
      if (!this.state.isConnected) {
        this.clearTimers()
        return
      }

      // 检查上次心跳是否超时
      if (this.pongTimeout) {
        console.warn('[WebSocket] 心跳响应超时，即将断开连接')
        this.disconnect()
        return
      }

      try {
        // 使用Ping消息而非文本消息
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
          // 发送心跳
          this.ws.send('ping')

          // 设置心跳超时检测
          this.pongTimeout = setTimeout(() => {
            this.pongTimeout = null
            console.warn('[WebSocket] 心跳响应超时')
            this.disconnect()
          }, this.config.heartbeatTimeout)
        }
      } catch (error) {
        this.handleError(error)
      }
    }, this.config.heartbeatInterval)
  }

  /**
   * 发送积压的消息
   * @private
   */
  flushPendingMessages() {
    while (this.data.pendingMessages.length > 0 && this.state.isConnected) {
      const message = this.data.pendingMessages.shift()
      this.send(message, { retry: false })
    }
  }

  /**
   * 清理所有定时器
   * @private
   */
  clearTimers() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
    if (this.pongTimeout) {
      clearTimeout(this.pongTimeout)
      this.pongTimeout = null
    }
  }

  /**
   * 触发事件
   * @param {string} event 事件名称
   * @param {any} [data] 事件数据
   * @private
   */
  emit(event, data) {
    // 触发通用事件监听器
    if (this.eventListeners.has(event)) {
      this.eventListeners.get(event).forEach(cb => cb(data))
    }

    // 触发消息处理器
    if (this.messageHandlers.has(event)) {
      this.messageHandlers.get(event).forEach(handler => handler(data))
    }
  }
}

// 创建新的ws客户端
function createWebSocketClient(url, options = {}) {
  return new WebSocketClient(url, options)
}

// 全局创建一个ws客户端单例实例
let webSocketClient = null

// 读取环境变量中的 WebSocket 开关设置
const isWebSocketEnabled = process.env.VUE_APP_WS_ENABLED === 'true'
if (isWebSocketEnabled) {
  webSocketClient = new WebSocketClient(process.env.VUE_APP_WS_URL, {
    autoConnect: true, // 自动连接
    reconnectLimit: 10, // 最多重试10次
    reconnectInterval: 1000, // 初始重连延迟1秒
    heartbeatInterval: 15000, // x秒发送一次心跳
    heartbeatTimeout: 5000 // x秒未响应视为超时
  })
}

export { createWebSocketClient, webSocketClient }